using System;
using System.Collections;
using System.Reflection;
using System.Xml;

namespace PaulRz.Util
{
	/// <summary>
	/// Makes working with null values simplier
	/// </summary>
	public class NullUtil
	{
		private static object CreateInstance(Type type,object parameter)
		{
			try
			{
				return Activator.CreateInstance(type, parameter==null? new object[0] : new object[] {parameter});
			} 
			catch {};
			return null;
		}

		private static object CreateInstance(Type type)
		{
            object newValue=CreateInstance(type, null);
			if(newValue==null)
				newValue=CreateInstance(type,0);
			if(newValue==null)
				newValue=CreateInstance(type, String.Empty);
			if(newValue==null)
				newValue=CreateInstance(type,new char[0]);
			return newValue;
		}

		private static void FillArray(Array arr)
		{
		    Type elementType = arr.GetType().GetElementType();
            for(int i=0;i<arr.Length;i++)
			{
				if(arr.GetValue(i)==null) 
				{
					arr.SetValue(CreateInstance(elementType),i);
				}
				Fill(arr.GetValue(i));
			}
		}

		public static void Fill(object obj)
		{
			if(obj==null || obj is string || obj is XmlNode || obj.GetType().IsPrimitive || obj is Hashtable)
				return;

		    PropertyInfo[] props = obj.GetType().GetProperties();

			foreach (PropertyInfo info in props)
			{
				if(!info.CanWrite || !info.CanRead)
					continue;

				if(info.GetValue(obj,null)==null) 
				{
					
					info.SetValue(obj,CreateInstance(info.PropertyType),null);
				}
				if(info.PropertyType.IsArray)
					FillArray((Array) info.GetValue(obj,null));
				else 
					Fill(info.GetValue(obj,null));
			}

		    FieldInfo[] fields = obj.GetType().GetFields();

			foreach (FieldInfo info in fields)
			{
				if(!info.IsPublic || info.IsStatic || info.IsLiteral)
					continue;
				
				if(info.GetValue(obj)==null) 
				{
					
					info.SetValue(obj,CreateInstance(info.FieldType));
				}
				if(info.FieldType.IsArray)
					FillArray((Array) info.GetValue(obj));
				else 
					Fill(info.GetValue(obj));
			}
			
		}

	}
}
